do
local _ENV = _ENV
package.preload[ "fixed" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
  the full license in the file LICENSE or by following the web link.

  FIXED - thorough fixed point operations in Lua

  REPRESENTATION

  A fixed point value is represented by a triad: `{ _v, _m, _n }`  `_v` is the
  raw  (unsigned integer) value used in calculations.  `_m` and `_n` are the
  integer and the fractional bit counts respectively.  The ARM variant of Q
  numbers is used for this.  Thus a q15.1 represents 15 bits of signed integer
  range and 1 bit of fractional range.  (In the TI variant that same value would
  be q14.1 as the sign bit is implied.)  Only Q and not UQ numbers are
  supported.

  Note that the triad is exposed only for debugging courtesy.  Manipulation of
  the values directly will result in failures both subtle and unsubtle.

  All viable operators are supported that the `number` type supports.  This
  means that all arithmetic operations are supported and all comparisons are
  supported.  These will transparently interact with the `number` type with
  said `number` being on the left or the right of the operator with one
  exception: `==`.  (The semantics of equality comparison in Lua and how they
  relate to metamethods mean that only two fixed numbers can be compared for
  equality.)

  A lot of extra operations are also provided.  Consult the README.md file for
  full details.

  There are two levels of constructor in this library.  The first one, returned
  by `require 'fixed'` will generate value factories.  It stores the desired
  `m` and `n` factors for numbers and each time it is called it will return an
  actual fixed number of that type.  It is typically used something like this:

    fixed = require 'fixed'
    -- a q15.1 number
    q15_1 = fixed(15, 1)

  The second level of constructor is a value constructor.  It is what the value
  factories use to create actual fixed values:

    fixed = require 'fixed'
    q15_1 = fixed(15, 1)
    a = q15_1(1.5)
    b = q15_1(2.2)

  All fixed numbers are interoperable, no matter what the `m` and `n` values
  given.  Conversions between them, however, will lose precision.

--############################################################################]]

local math, string = math, string

--[[###################################
  Caches and numbers.
--###################################]]
local qcache = {}
local vmcache = {}
local tcache = {}
local MAX_BITS = 63

--[[###################################
  Module-local helper functions
--###################################]]

-- generate an n-bit mask
local function mask(bits)
    return (1 << bits) - 1
end

-- convert an unsigned value of given bit width to signed
local function to_signed(value, bits)
    if value >= (1 << (bits - 1)) then
        return value - (1 << bits)
    else
        return value
    end
end

-- convert a signed value of given bit width to unsigned
local function from_signed(value, bits)
    -- Convert signed value to unsigned value
    if value < 0 then
        return value + (1 << bits)
    else
        return value
    end
end

-- convert fixed value to double (Lua-native) value
local function fixed_to_double(q)
    local signed_value = to_signed(q._v, q._m + q._n)
    return (signed_value * 1.0) / (1 << q._n)
end

-- convert double (Lua-native) value to fixed value
local function double_to_fixed(d, n)
    return math.floor(d * (1 << n) + 0.5)
end

local function check_fixed_type(q)
    return type(q) == 'table' and
           q._v ~= nil and math.type(q._v) == 'integer' and
           q._m ~= nil and math.type(q._m) == 'integer' and
           q._n ~= nil and math.type(q._n) == 'integer' and
           string.match(getmetatable(q)['__type__'],'^q%d+%.%d+$')
end

local function normalize_numbers(v1, v2, err_msg)
    if type(v1) == 'number' then
        local v = double_to_fixed(v1, v2._n)
        v1 = v2:clone()
        v1._v = v
    elseif type(v2) == 'number' then
        local v = double_to_fixed(v2, v1._n)
        v2 = v1:clone()
        v2._v = v
    elseif not check_fixed_type(v2) then
        error(err_msg)
    end
    return v1, v2
end

local function match_numbers(q1, q2)
    local r = q1:clone()
    if q1._n ~= q2._n then
        local common_n = math.max(q1._n, q2._n)
        local temp = q2:clone()
        temp:rescale(q2._m, common_n)
        q2 = temp
        r:rescale(q1._m, common_n)
    end
    return r, q2
end

local function build_value_metatable(m, n, type_str)
    return {
        -- OPERATIONS
        __index = {
            -- type checking operations
            get_type = function(q)
                return type_str
            end,

            -- output and display operations
            printv = function(q)
                print(string.format('m = %d, n = %d, v = %x -> %f', q._m, q._n, q._v, q()))
            end,

            serialize = function(q)
                return string.format('fixed:%d:%d:%d', q._m, q._n, q._v)
            end,

            to_hex = function(q)
                return string.format('0x%x', q._v)
            end,

            to_string = function(q)
                return string.format('q%d.%d(%f)', q._m, q._n, fixed_to_double(q))
            end,

            -- format manipulation operations
            clone = function(q)
                return setmetatable({
                    _v = q._v,
                    _m = q._m,
                    _n = q._n }, getmetatable(q))
            end,

            mask = function(q)
                q._v = q._v & mask(q._m + q._n)
            end,

            rescale = function(q, new_m, new_n)
                local v = q._v
                local m = q._m
                local n = q._n
                local total_bits = m + n

                -- Adjust fractional bits
                if new_n > n then
                    v = v << (new_n - n)
                elseif new_n < n then
                    -- Round when reducing fractional bits
                    local round = 1 << (n - new_n - 1)
                    v = (v + round) >> (n - new_n)
                end

                -- Adjust integer bits
                local new_total = new_m + new_n
                if new_total > total_bits then
                    -- Extend sign if needed
                    local sign_bit = v & (1 << (total_bits - 1))
                    if sign_bit ~= 0 then
                        local extension = mask(new_total - total_bits)
                        v = v | (extension << total_bits)
                    end
                elseif new_total < total_bits then
                    -- Mask to new size
                    v = v & mask(new_total)
                end

                q._v = v
                q._m = new_m
                q._n = new_n
            end,

            saturate = function(q)
                local total_bits = q._m + q._n
                local max_unsigned = mask(total_bits)
                local raw_max = mask(total_bits - 1)

                if q._v > max_unsigned then
                    q._v = raw_max
                end

                return q
            end,

            -- predicate tests
            check_overflow = function(q)
                local total_bits = q._m + q._n
                local value = q._v

                -- Check if value exceeds the representable range
                if (value & ~mask(total_bits)) ~= 0 then
                    return true
                end

                return false
            end,

            is_zero = function(q)
                return q._v == 0
            end,

            is_positive = function(q)
                local signed_value = to_signed(q._v, q._m + q._n)
                return signed_value > 0
            end,

            is_negative = function(q)
                local signed_value = to_signed(q._v, q._m + q._n)
                return signed_value < 0
            end,

            -- arithmetic operations (non-operator)
            abs = function(q)
                local r = q:clone()
                local signed_value = to_signed(r._v, r._m + r._n)
                r._v = from_signed(math.abs(signed_value), r._m + r._n)
                return r
            end,

            -- conversion operations
            to_number = function(q)
                return fixed_to_double(q)
            end,

            to_integer = function(q)
                local signed_value = to_signed(q._v, q._m + q._n)
                return math.floor(signed_value / (1 << q._n))
            end,

            to_fraction = function(q)
                return q._v & mask(q._n)
            end,
        },

        -- OPERATORS
        __add = function(q1, q2)
            q1, q2 = normalize_numbers(q1, q2, 'attempt to add fixed-point with non-numeric value')
            q1, q2 = match_numbers(q1, q2)

            local signed_v1 = to_signed(q1._v, q1._m + q1._n)
            local signed_v2 = to_signed(q2._v, q2._m + q2._n)
            local result = signed_v1 + signed_v2
            q1._v = from_signed(result, q1._m + q1._n)

            q1:mask()
            if q1:check_overflow() then
                q1:saturate()
            end
            return q1
        end,

        __sub = function(q1, q2)
            q1, q2 = normalize_numbers(q1, q2, 'attempt to subtract fixed-point with non-numeric value')
            q1, q2 = match_numbers(q1, q2)

            local signed_v1 = to_signed(q1._v, q1._m + q1._n)
            local signed_v2 = to_signed(q2._v, q2._m + q2._n)
            local result = signed_v1 - signed_v2
            q1._v = from_signed(result, q1._m + q1._n)

            q1:mask()
            if q1:check_overflow() then
                q1:saturate()
            end
            return q1
        end,

        __mul = function(q1, q2)
            q1, q2 = normalize_numbers(q1, q2, 'attempt to multiply fixed-point with non-numeric value')
            q1, q2 = match_numbers(q1, q2)

            -- Convert to signed values for arithmetic
            local signed_v1 = to_signed(q1._v, q1._m + q1._n)
            local signed_v2 = to_signed(q2._v, q2._m + q2._n)

            -- Multiply and then shift right by n to maintain scale
            local intermediate = signed_v1 * signed_v2
            local result = intermediate >> q1._n
            q1._v = from_signed(result, q1._m + q1._n)

            q1:mask()
            if q1:check_overflow() then
                q1:saturate()
            end
            return q1
        end,

        __div = function(q1, q2)
            q1, q2 = normalize_numbers(q1, q2, 'attempt to divide fixed-point with non-numeric value')
            q1, q2 = match_numbers(q1, q2)

            local result = fixed_to_double(q1) / fixed_to_double(q2)
            q1._v = double_to_fixed(result, q1._n)

            q1:mask()
            if q1:check_overflow() then
                q1:saturate()
            end
            return q1
        end,

        __mod = function(q1, q2)
            q1, q2 = normalize_numbers(q1, q2, 'attempt to mod fixed-point with non-numeric value')
            q1, q2 = match_numbers(q1, q2)

            local signed_v1 = to_signed(q1._v, q1._m + q1._n)
            local signed_v2 = to_signed(q2._v, q2._m + q2._n)
            local result = signed_v1 % signed_v2
            q1._v = from_signed(result, q1._m + q1._n)

            q1:mask()
            return q1
        end,

        __pow = function(q1, q2)
            if (not check_fixed_type(q1) and type(q1) ~= 'number') or
               (not check_fixed_type(q2) and type(q2) ~= 'number') then
                error 'attempt to pow fixed-point with non-numeric value'
            end
            local r, v1, v2
            if check_fixed_type(q1) and check_fixed_type(q2) then
                q1, q2 = match_numbers(q1, q2)
            end
            if check_fixed_type(q1) then
                r = r or q1:clone()
                v1 = fixed_to_double(q1)
            else
                v1 = q1
            end
            if check_fixed_type(q2) then
                r = r or q2:clone()
                v2 = fixed_to_double(q2)
            else
                v2 = q2
            end

            -- Use floating point for non-integer exponents
            local result = v1 ^ v2
            r._v = double_to_fixed(result, r._n)
            r:mask()
            return r
        end,

        __unm = function(q)
            local r = q:clone()
            local signed_value = to_signed(r._v, r._m + r._n)
            r._v = from_signed(-signed_value, r._m + r._n)
            r:mask()
            if r:check_overflow() then
                r:saturate()
            end
            return r
        end,

        __concat = function(a, b)
            return tostring(a) .. tostring(b)
        end,

        __eq = function(q1, q2)
            if not check_fixed_type(q2) then
                return false
            end
            q1, q2 = match_numbers(q1, q2)
            return q1._v == q2._v
        end,

        __lt = function(q1, q2)
            q1, q2 = normalize_numbers(q1, q2, 'attempt to compare fixed-point with non-numeric value')
            q1, q2 = match_numbers(q1, q2)

            local signed_v1 = to_signed(q1._v, q1._m + q1._n)
            local signed_v2 = to_signed(q2._v, q2._m + q2._n)
            return signed_v1 < signed_v2
        end,

        __le = function(q1, q2)
            q1, q2 = normalize_numbers(q1, q2, 'attempt to compare fixed-point with non-numeric value')
            q1, q2 = match_numbers(q1, q2)

            local signed_v1 = to_signed(q1._v, q1._m + q1._n)
            local signed_v2 = to_signed(q2._v, q2._m + q2._n)
            return signed_v1 <= signed_v2
        end,

        __call = function(q)
            return fixed_to_double(q)
        end,

        __tostring = function(q)
            return string.format('q%d.%d(%f)', q._m, q._n, fixed_to_double(q))
        end,

        -- TYPE IDENTITY
        __type__ = type_str,
    }
end

local function build_constructor_methods(type_str)
    return {
        zero = function(self)
            if not tcache[type_str].zero then tcache[type_str].zero = self(0) end
            return tcache[type_str].zero
        end,

        one = function(self)
            if not tcache[type_str].one then tcache[type_str].one = self(1) end
            return tcache[type_str].one
        end,

        max_value = function(self)
            if not tcache[type_str].max_value then
                local raw = mask(self._m + self._n - 1)
                tcache[type_str].max_value = self:from_raw(raw)
            end
            return tcache[type_str].max_value
        end,

        min_value = function(self)
            if not tcache[type_str].min_value then
                local raw = 1 << (self._m + self._n - 1)
                tcache[type_str].min_value = self:from_raw(raw)
            end
            return tcache[type_str].min_value
        end,

        from_raw = function(self, raw_value)
            local instance = self(0)
            instance._v = raw_value
            if instance:check_overflow() then
                error(string.format('raw_value = %d, max_value = %d', raw_value, self:max_value()._v))
            else
                return instance
            end
        end,

        get_precision = function(self)
            return self._m, self._n
        end,

        from_serialized = function(self, str)
            local m, n, v = str:match('fixed:(%d+):(%d+):(%d+)')
            if not m then error('Invalid serialized format') end
            m, n, v = tonumber(m), tonumber(n), tonumber(v)
            if m ~= self._m or n ~= self._n then error('Type mismatch in deserialization') end
            return setmetatable({_v = v, _m = m, _n = n}, getmetatable(self(0)))
        end,
    }
end

--[[###################################
  Builder constructor.
  'q15.1' in ARM notation is called `new(15, 1)`
  ###################################]]
local function new(_, m, n)

    assert(math.type(m) == 'integer' and math.type(n) == 'integer', 'm and n must be integers')
    assert(m >= 1, 'm (integer bits) must be at least 1 (for sign bit)')
    assert(n >= 0, 'n (fractional bits) must be non-negative')
    assert(m + n < MAX_BITS, string.format('Total bits must be less than %d (got %d+%d=%d)', MAX_BITS, m, n, m+n))

    local _m = m
    local _n = n
    local total_bits = m + n
    local type_str = string.format('q%d.%d', _m, _n)

    -- value constructor
    local function newv(q, v)
        local raw_value
        if type(v) == 'number' then
            raw_value = double_to_fixed(v, _n)
        elseif check_fixed_type(v) then
            -- Direct conversion between different fixed-point formats
            if _n == v._n then
                raw_value = v._v
            else
                local shift = _n - v._n
                if shift > 0 then
                    raw_value = v._v << shift
                else
                    -- Round when reducing fractional bits
                    local round = 1 << (-shift - 1)
                    raw_value = (v._v + round) >> (-shift)
                end
            end
        else
            error('Invalid value type: ' .. type(v))
        end

        -- Create the metatable for the value based on the type string
        if not vmcache[type_str] then
            vmcache[type_str] = build_value_metatable(_m, _n, type_str)
        end

        local r = setmetatable({
            _v = raw_value,
            _m = _m,
            _n = _n,
        }, vmcache[type_str])

        r:mask()
        return r
    end

    if not qcache[type_str] then
        tcache[type_str] = {}

        -- Create the type constructor with additional methods
        local constructor_methods = build_constructor_methods(type_str)
        local constructor = setmetatable({
            _m = _m,
            _n = _n,
        }, {
            __call = newv,
            __type__ = type_str,
            __index = constructor_methods,
        })
        qcache[type_str] = constructor
    end

    return qcache[type_str]
end

return setmetatable({}, { __call = new })
end
end

do
local _ENV = _ENV
package.preload[ "muban" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
  the full license in the file LICENSE or by following the web link.

  MUBAN - a templating system written in Lua focused on code generation.

    Future Growth:

    Currently this library is useful and powerful.  What is needed in the future
    however needs some annotation.

    - An optional mechanism for escaping the generated text for specific targets
      might be nice.  For example if generating HTML it might be nice to have:

            & -> &amp;
            < -> &lt;
            > -> &gt;
            " -> &quot;
            ' -> &#39;'

      Such a mechanism would supply defaults for common targets (Lua, HTML, C,
      etc.) as well as a way to register their own escaping function.

    - More ambitiously the ability for users to form custom blocks would be
      interesting.  Currently there are four block distinguishing characters
      giving us the {{=, {{!, {{- and {{# blocks.  There are plenty of other
      symbol characters we can use.  Perhaps the ability to register such user
      customized blocks would be useful.
--############################################################################]]

local table = require 'tablex'
local M = {}

-- build throwable errors with extra information available
local function build_exception(...)
    local t = { ... }
    return setmetatable(t, {
        __tostring = function(self)
            if self.trace then
                return string.format('%s\nInclude trace:\n  %s',
                    string.format(table.unpack(self)),
                    table.concat(self.trace, ' -> '))
            end
            return string.format(table.unpack(self))
        end
    })
end

-- run a compiled template to generate the resulting text
local function run(chunks, env)

    -- build a sandbox for legal operations
    -- this will be critical for future versions which will permit user
    -- customization
    local sandbox = {
        tostring = tostring,
        table = table,
        pairs = pairs,
        ipairs = ipairs,
        _source = {}  -- Output buffer
    }

    -- import the user-supplied variables for use in expansions
    if env then
        for k, v in pairs(env) do
            sandbox[k] = v
        end
    end

    -- build up the source code that will generate our final text
    -- each chunk type/format decrees how the source code is provided
    local body = {'local _source = {}'}
    for _, chunk in ipairs(chunks) do
        if chunk.type == 'text' then
            table.insert(body, string.format('_source[#_source + 1] = %q', chunk.value))
        elseif chunk.type == 'code' then
            if chunk.format == '=' then
                if sandbox[chunk.value] ~= nil then
                    table.insert(body, string.format('_source[#_source + 1] = tostring(%s)', chunk.value))
                else
                    error(build_exception('runtime error: variable "%s" missing', chunk.value))
                end
            else -- '!'
                table.insert(body, chunk.value)  -- Raw Lua code
            end
        end
    end
    table.insert(body, 'return table.concat(_source)')
    local full_code = table.concat(body, '\n')

    -- source now completed by assembled chunks, compile the ensuing code in Lua
    local fn, err = load(full_code, '=(template)', 't', sandbox)
    if not fn then
        error(build_exception('template compile error: %s', err))
    end

    -- and run the prepared chunk
    local success, result = pcall(fn)
    if not success then
        error(build_exception('template runtime error: %s', result))
    end

    return result
end

-- break down a template into a collection of differently-typed 'chunks'
-- each 'chunk' will be dealt with differently in the runner
-- the first argument is the module table and should be ignored
-- the second argument is the text of the template
-- the third argument is an optional compilation context used to help with meaningul error messages
-- the fourth argument is a stealth argument used in {{#...}} for inclusion
local function compile(_, template, compile_ctx, chunks)
    chunks = chunks or {}
    compile_ctx = compile_ctx or {}
    compile_ctx.filename = compile_ctx.filename or '(string)'
    compile_ctx.line = compile_ctx.line or 1
    compile_ctx.include_stack = compile_ctx.include_stack or {}

    local pos = 1
    while pos <= #template do
        -- Find next template tag
        local begin = template:find('{{', pos)
        if not begin then
            -- Add remaining text as literal chunk
            if pos <= #template then
                table.insert(chunks, { type = 'text', value = template:sub(pos) })
            end
            break
        end

        -- Track line numbers
        local newline_count = select(2, template:sub(pos, begin - 1):gsub('\n', '\n'))
        compile_ctx.line = compile_ctx.line + newline_count

        -- Check for escaped tag
        local is_escaped = begin > 1 and template:sub(begin - 1, begin - 1) == '\\'
        local has_double_backslash = begin > 2 and template:sub(begin - 2, begin - 1) == '\\\\'

        -- Add text before the block (handling backslashes)
        if begin > pos then
            local text_before = template:sub(pos, begin - (is_escaped and 2 or 1))
            if has_double_backslash then
                -- Replace '\\' with '\' before the block
                text_before = text_before:sub(1, -2) .. '\\'
            end
            table.insert(chunks, { type = 'text', value = text_before })
        end

        if is_escaped and not has_double_backslash then
            -- Literal '{{'
            table.insert(chunks, { type = 'text', value = '{{' })
            pos = begin + 2
        else
            -- Process template tag
            local block_end = template:find('}}', begin)
            if not block_end then
                error(build_exception(
                    'in %s:%d: unclosed template tag',
                    compile_ctx.filename,
                    compile_ctx.line
                ))
            end

            local block_content = template:sub(begin + 2, block_end - 1)
            local tag = block_content:sub(1, 1)
            local content = block_content:sub(2)

            if tag == '=' or tag == '!' then
                table.insert(chunks, {
                    type = 'code',
                    value = content,
                    format = tag
                })
            elseif tag == '#' then
                content = string.match(content, '^[%s]*(.-)[%s]*$')
                for _, included in ipairs(compile_ctx.include_stack) do
                    if included.filename == content then
                        local err = build_exception(
                            'in %s:%d: circular include "%s" (first seen at %s:%d)',
                            compile_ctx.filename,
                            compile_ctx.line,
                            content,
                            included.filename,
                            included.line
                        )
                        error(err)
                    end
                end

                local file, file_err = io.open(content, 'r')
                if not file then
                    error(build_exception(
                        'in %s:%d: failed to open "%s": %s',
                        compile_ctx.filename,
                        compile_ctx.line,
                        content,
                        file_err
                    ))
                end

                local new_include_stack = {
                    {
                        filename = compile_ctx.filename,
                        line = compile_ctx.line
                    }
                }
                for _, entry in ipairs(compile_ctx.include_stack) do
                    table.insert(new_include_stack, entry)
                end

                compile(_, file:read('*a'), {
                    filename = content,
                    line = 1,
                    include_stack = new_include_stack
                }, chunks)
                file:close()
            elseif tag == '-' then
                -- Comment (ignore)
            else
                error(build_exception(
                    'in %s:%d: unknown tag "{{%s"',
                    compile_ctx.filename,
                    compile_ctx.line,
                    tag
                ))
            end
            pos = block_end + 2
        end
    end

    return setmetatable(chunks, { __call = run })
end

return setmetatable({}, { __call = compile })
end
end

do
local _ENV = _ENV
package.preload[ "pobject" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
  the full license in the file LICENSE or by following the web link.

  POBJECT - simple prototype-based object system for Lua
--############################################################################]]

--[[####################################
  Exception objects with printable forms.
--####################################]]

-- build throwable errors with extra information available
local function build_exception(string_representation, ...)
    return setmetatable({...}, { __tostring = function() return string_representation end })
end

-- exceptions thrown by this library
local ERR_ARRAY_INITIALIZER    = build_exception('initializers cannot be array-flavoured tables')
local ERR_DESTROYED            = build_exception('pobject destroyed')
local ERR_FIXED                = build_exception('cannot change fixed object')
local ERR_FUNDAMENTAL          = build_exception('cannot override fundamental operations')
local ERR_NONTABLE_INITIALIZER = build_exception('initializers must be be in a table')

--[[####################################
  Module-scope registry variables.
--####################################]]

-- foward declaration for protected fundamental operations
local fundamental_operations = {}

-- storage for cloned local values
local values = {}

-- weak-keyed storage for children
local children = setmetatable({}, { __mode = "k" })

-- weak-keyed storage for observers
local observers = setmetatable({}, { __mode = "k" })

--[[####################################
  Local helper functions.
--####################################]]

-- shallow table merge
local function merge(d, s)
    if type(d) == 'table' and type(s) == 'table' then
        for k,v in pairs(s) do
            d[k] = v
        end
    end
    return d
end

-- shallow clone of a table of values
local function clone(s)
    return merge({}, s)
end

-- check possible parental relationship between two objects
local function check_relationship(child, parent_prospect)
    -- start with the immediate parent
    local current_ancestor = values[child].parent

    while current_ancestor ~= nil do
        if current_ancestor == parent_prospect then
            return true
        end
        current_ancestor = values[current_ancestor] and values[current_ancestor].parent
    end

    return false
end

local function build_metatable(has_parent)
    return {
        __index = function(t, k)
            if fundamental_operations and fundamental_operations[k] then
                return fundamental_operations[k]
            elseif values[t] and values[t].locals and values[t].locals[k] then
                return values[t].locals[k]
            elseif has_parent and values[t] and values[t].parent and values[t].parent[k] then
                return values[t].parent[k]
            else
                return nil
            end
        end,

        __newindex = function(t, k, v)
            assert(fundamental_operations[k] == nil, ERR_FUNDAMENTAL)
            values[t].locals[k] = v
        end,

        __type = "pobject"
    }
end

--[[####################################
  Module initializer function.
--####################################]]

local function new(_module, base_table)
    assert(base_table == nil or type(base_table) == 'table', ERR_NONTABLE_INITIALIZER)
    assert(base_table == nil or #base_table == 0, ERR_ARRAY_INITIALIZER)

    local id = {}
    values[id] = {
        locals = clone(base_table) or {},
        parent = {},
    }

    return setmetatable(id, build_metatable(false))
end

--[[####################################
  pobject methods.
--####################################]]

-- make a child by cloning a parent, adding extra/changed members optionally
fundamental_operations.clone = function(parent, members)
    local id = {}
    values[id] = {
        locals = clone(members) or {},
        parent = parent or {},
    }

    children[parent] = children[parent] or {}
    table.insert(children[parent], id)

    return setmetatable(id, build_metatable(true))
end

-- mix in a package of operations and/or values
fundamental_operations.using = function(self, mixin)
    values[self].locals = merge(values[self].locals, mixin)
    return self
end

-- make a pobject read-only
-- note that if the parent is not fixed, non-overridden members can be changed
-- and new keys added
fundamental_operations.fix = function(self)
    mt = getmetatable(self)
    mt.__newindex = function() error(ERR_FIXED) end
    return setmetatable(self, mt)
end

-- re-root an object into an independent prototype with a flattened membership
-- tree
fundamental_operations.sever = function(self)
    -- start with our immediate parent
    local current_ancestor = values[self].parent

    -- merge ancestral values
    merge_values = {}
    while current_ancestor ~= nil do
        -- if our parent has values, clone them and merge them down
        if values[current_ancestor] and values[current_ancestor].locals then
            ancestor_clone = clone(values[current_ancestor].locals or {})
            merge_values = merge(ancestor_clone, merge_values)
        end
        -- get our next ancestor
        current_ancestor = values[current_ancestor] and values[current_ancestor].parent
    end

    -- finally merge all ancestors into current locals
    merge_values = merge(merge_values, values[self].locals or {})
    values[self].locals = merge_values

    -- now emancipate ourselves from our family
    values[self].parent = {}
    return setmetatable(self, build_metatable(false))
end

-- destroy an object, ensuring its resources are freed
fundamental_operations.destroy = function(self)
    if children[self] ~= nil then
        for _,v in ipairs(children[self]) do
            v:sever()
        end
        children[self] = nil
    end
    values[self].locals = nil
    values[self].parent = nil
    values[self] = nil
    local mt = getmetatable(self)
    mt.__index = function(t,k) error(ERR_DESTROYED, 2) end
    mt.__newindex = function(t,k,v) error(ERR_DESTROYED, 2) end
end

-- provide a tabular portrayal of members in the ancestor tree
-- provided table can be printed in readable form
fundamental_operations.introspect = function(self)
    -- build an indent block
    local function indent(n)
        return string.rep(' ', n)
    end

    -- walk the chain of ancestors
    local function ancestors()
        local parent = values[self].parent
        if parent ~= nil and parent.introspect ~= nil then
            return parent:introspect()
        else
            return nil
        end
    end

    -- provide a pretty string representation of an object's entire inheritance hierarchy
    local seen = {}
    local function to_string(t, i)
        if t == nil then return '' end

        local ii = i or 0

        local str = indent(ii) .. (ii == 0 and 'object' or 'parent') .. (string.format('<%p> = \n', t))
        if seen[t] then
            str = str .. ('{ ... <circular> ... }\n')
        else
            seen[t] = true
            str = str .. indent(ii) .. '{\n'
            for k,v in pairs(t.locals) do
                str = str .. indent(ii + 2) .. tostring(k) .. ' -> ' .. tostring(v) .. '\n'
            end
            str = str .. to_string(t.parent, ii + 2)
            str = str .. indent(ii) .. '}\n'
        end
        if i == nil then
            seen = {}
        end

        return str
    end

    local rv = setmetatable({}, { __tostring = to_string })
    rv.locals = clone(values[self].locals)
    rv.parent = ancestors()

    return rv
end

-- predicate: is self a descendant of the candidate object
fundamental_operations.descendant_of = function(self, candidate)
    return check_relationship(self, candidate)
end

-- predicate: is self an ancestor of the candidate object
fundamental_operations.ancestor_of = function(self, candidate)
    return check_relationship(candidate, self)
end

-- attach access observers to properties/methods and pre/post wrappers to methods
fundamental_operations.observe = function(self, config)
    observers[self] = observers[self] or {}
    observers[self].props = observers[self].props or {}
    observers[self].methods = observers[self].methods or {}
    observers[self].props = merge(observers[self].props, config and config.props or {})
    observers[self].methods = merge(observers[self].methods, config and config.methods or {})

    -- Only patch metatable once
    if not getmetatable(self).__observed then
        local mt = getmetatable(self)
        local old_index = mt.__index
        local old_newindex = mt.__newindex

        -- Property access
        mt.__newindex = function(t, key, value)
            local obs = observers[t]
            if obs and obs.props[key] and obs.props[key].set then
                obs.props[key].set(self, key, value)
            end
            old_newindex(t, key, value)
        end

        -- Method/Property lookup
        mt.__index = function(t, key)
            local value = old_index(t, key)

            -- Method wrapping
            if observers[t] and observers[t].methods[key] and type(value) == "function" then
                local method = value
                value = function(...)
                    local pre = observers[t].methods[key].pre
                    local post = observers[t].methods[key].post
                    if pre then pre(key, ...) end
                    local results = { method(...) }
                    if post then post(key, ...) end
                    return table.unpack(results)
                end
            -- Property access
            elseif observers[t] and observers[t].props[key] and observers[t].props[key].get then
                observers[t].props[key].get(self, key, value)
            end

            return value
        end

        mt.__observed = true -- Mark as patched
        setmetatable(self, mt)
    end

    return self
end

--[[####################################
  # Module return is an empty table with a __call override pointing to the constructor.
  ####################################]]

return setmetatable({}, { __call = new })
end
end

do
local _ENV = _ENV
package.preload[ "proxify" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
  the full license in the file LICENSE or by following the web link.

  PROXIFY - simple table proxying in Lua
--############################################################################]]


--[[####################################
  The general-purpose wrapper.  All other proxies are written in terms of this.
--####################################]]
local metametatable = {
    nope = 'not gonna happen'
}
local function wrap(tbl, idx, nidx, mt)

    assert(type(tbl) == 'table', 'only tables are proxified')
    assert(type(idx) == 'nil' or type(idx) == 'function', 'the index function must be absent or a function')
    assert(type(nidx) == 'nil' or type(nidx) == 'function', 'the newindex function must be absent or a function')
    assert(idx or nidx, 'at least one of the indexing operations must be supplied')
    assert(type(mt) == 'nil' or type(mt) == 'table', 'user-supplied metatable must be a table')

    local i, n

    if idx then
        i = function(_, k)
            return idx(tbl, k)
        end
    else
        i = function(_, k) return tbl[k] end
    end

    if nidx then
        n = function(_, k, v)
            nidx(tbl, k, v)
        end
    else
        n = function(_, k, v) tbl[k] = v end
    end

    local function l(_)
        return #tbl
    end

    if not mt then mt = {} end
    mt.__index = i;
    mt.__newindex = n;
    mt.__len = mt.__len or l
    mt.__metatable = metametatable

    return setmetatable({}, mt)
end

--[[####################################
  The read-only proxy.
--####################################]]
local function ro(tbl)
    n = function()
        error('attempt to update read-only table', 0)
    end
    return wrap(tbl, nil, n)
end

--[[####################################
  The access-logging proxy.
--####################################]]
local function logger(tbl, cb)
    local function i(t, k)
        cb('get', t, k, t[k])
        return t[k]
    end
    local function n(t, k, v)
        cb('set', t, k, v)
        t[k] = v
    end
    return wrap(tbl, i, n)
end

--[[####################################
  The counter proxy.
--####################################]]
local function counter(tbl)
    local count = 0
    for k,_ in pairs(tbl) do
        count = count + 1
    end

    local function n(t, k, v)
        if t[k] == nil and v ~= nil then
            count = count + 1
        elseif t[k] ~= nil and v == nil then
            count = count - 1
        end
        t[k] = v
    end

    local function l(_)
        return count
    end

    return wrap(tbl, nil, n, { __len = l })
end

local M = {
    wrap = wrap,
    ro = ro,
    logger = logger,
    counter = counter,
}

return setmetatable({}, { __index = M })
end
end

do
local _ENV = _ENV
package.preload[ "rexcept" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  REXCEPT - Restartable EXCEPTions in Lua.

  Lua's error primitives are exactly this: primitive.  They are not particularly
  conducive to chaining exceptions, for example, and they are also incapable of
  doing in-situ retries: entire, potentially time-consuming operations must be
  repeated for errors which may be trivially correctable.

  This system replaces pcall() and error() with try() and either raise() for
  restartable exceptions or throw() for non-restartable.  Handlers can be
  changed and rethrowing is a valid option for C++- and Java-style exception
  handling discipline.  In addition, a handler can clear up an error condition
  (out of memory, say, or non-existent file) and tell the block that threw to
  try again without having to unwind a potentially large stack and rewind it to
  do it over from the beginning.

  Helper functions

  API
  ===

  `try(block, handlers)` - execute block and on error execute a handler keyed by
                           the thrown/raised exception
  `raise(exception)` - user level restartable exception
  `throw(exception)` - user level terminal exception
  `exception(message)` - build optional "smart exceptions" to include data

  ############################################################################]]

local counter = 1
local string = string
local table = table
local table = require 'tablex'

local function is_exception(exc)
    if exc.__type == 'exception' and exc.__name then
        return true
    else
        return false
    end
end

--[[####################################
  try(block, handlers)
  Speculatively execute `block` and, upon error, call the appropriate handler
  if available.  If an `exception()` was `raise()`ed, the handler is called and
  if returning 'true' the coroutine is `resume()`ed.  If it was `throw()`n it is
  not resumed and the coroutine is destroyed.
  ####################################]]
local function try(block, handlers)
    handlers = handlers or {}
    assert(type(block) == 'function', 'block must be a function')
    assert(type(handlers) == 'table', 'handlers must be in a table')
    for k,v in pairs(handlers) do
        assert(type(v) == 'function', 'handlers must be blocks')
    end

    block_wrapper = coroutine.create(block)
    local results = { coroutine.resume(block_wrapper) }
print(table.dump(results))
    if not results[1] then
        table.remove(results, 1)
        if is_exception(results[1]) then
            exc = results[1]
            if handlers[exc.__name] then
                if exc.__restartable then
                    results = { handlers[exc.__name](exc) }
                    if results[1] then
                        table.remove(results, 1)
                        coroutine.resume(block-wrapper, table.unpack(results))
                    end
                else
                    handlers[exc.__name](exc)
                end
            end
        elseif handlers['legacy'] then
            handlers['legacy'](table.unpack(results))
        end
        coroutine.close(block_wrapper)
    end

    return
end

local function validate_exception(exc)
    assert(is_exception(exc))
end

local function throw(exc)
    validate_exception(exc)
    exc.__restartable = false
    error(exc)
end

local function raise(exc)
    validate_exception(exc)
    exc.__restartable = true
    results = table.unpack { coroutine.yield(false, exc) }
end

local function exception(name)
    assert(type(name) == 'string')
    return setmetatable({
        __name = name,
        __type = 'exception',
    }, {
        __tostring = function(self)
            local rv = name .. '{data={'
            if self.data then
                for k, v in pairs(self.data) do
                    rv = rv .. tostring(k) .. '='
                    if type(v) == 'string' then rv = rv .. "'" end
                    rv = rv .. tostring(v)
                    if type(v) == 'string' then rv = rv .. "'" end
                    rv = rv .. ','
                end
            end
            rv = rv .. '},'
            if self.extra then
                for _, v in ipairs(self.extra) do
                    if type(v) == 'string' then rv = rv .. "'" end
                    rv = rv .. tostring(v)
                    if type(v) == 'string' then rv = rv .. "'" end
                    rv = rv .. ','
                end
            end
            return rv
        end,
        __call = function(self, data, ...)
            self.data = data
            self.extra = {...}
            self.__stack = debug.traceback(nil, 2)
            return self
        end,
    })
end

--[[############################################################################
  Since the 'return true for retry, false for abort' rule is not immediately
  obvious, and since encoding special values for these cases will make code
  messy and interact poorly with pcall() and other such facilities, it's easier
  to make special functions that do the job directly.
  ############################################################################]]
local function retry(...)
    return true, ...
end

local function abort()
    return false
end

--[[############################################################################
  The module table.
  ############################################################################]]
local M = {
    exception = exception,
    try = try,
    throw = throw,
    raise = raise,
    retry = retry,
    abort = abort,
}

--[[############################################################################
  Return an indirect table to protect from casual inspection of contents.
  ############################################################################]]
return setmetatable({}, { __index = M })
end
end

do
local _ENV = _ENV
package.preload[ "set" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
  the full license in the file LICENSE or by following the web link.

  SET - flexible set operations in Lua

  REPRESENTATION

    A set is represented with two tables: r and s.  r is indexed by the key and
    returns an index into s.  The key is stored in s at the given index.  This
    representation is chosen for reasons of time performance.  Two tables wastes
    a bit of space, and makes insertion marginally more expensive (two O(1)
    table operations instead of one), but for that removal is also O(1).

    Insertion involves pushing the value to the end of the s table and then
    pairing that last index with the value in r.  Membership checking is as
    simple as looking up the value in r and returning true if a number is
    provided.

    Removal is a bit more involved, but for that O(1).  First membership is
    checked (O(1)).  All subsequent steps assume membership is there.  The
    value is then stubbed out of r (O(1)).  The last value is popped off of the
    table s (O(1)).  If that value doesn't match the erased key, r is indexed
    by the popped value and given the value of where s contained the removed
    value (O(1)), followed by s[i] being given the value that was popped (O(1)).

    Thus, with this representation, at a slight cost in memory, sets have O(1)
    insertion, O(1) membership tests, and O(1) removal (albeit at wildly
    different K factors).

    Note that the set exposes its members (in arbitrary order) as a read-only,
    array-flavoured table.  This permits it to be used directly in most table
    reading operations.  Changing it with rawset is possible but will almost
    certainly break the set operations.
--############################################################################]]

--[[###################################
    Constructor.  Accepts any number of arbitrary arguments as the starting values
    of the set.
--###################################]]
local function new(_, ...)
    local r = {}
    local s = {}
    local protected = true
    local instantiated = false

    local function is_set_impl(s)
        return getmetatable(s) and getmetatable(s).__type__ == 'set'
    end

    setmetatable(s, {
        -- API operations
        __index = {
            -- single value insertion
            insert = function(ss, v)
                assert(is_set_impl(ss), 'self is not a set')
                assert(v ~= 'nil', 'cannot have nil as member')
                if not r[v] then
                    protected = false
                    table.insert(ss, v)
                    protected = true
                    r[v] = #ss
                end
                return ss
            end,

            -- single value removal
            remove = function(ss, v)
                assert(is_set_impl(ss), 'self is not a set')
                local i = r[v]
                if i then
                    r[v] = nil
                    protected = false
                    local t = table.remove(ss)
                    protected = true
                    if t ~= v then
                        r[t] = i
                        ss[i] = t
                    end
                end
                return ss
            end,

            -- multi-value insertion
            batch_insert = function(ss, ...)
                assert(is_set_impl(ss), 'self is not a set')
                for _,v in pairs {...} do
                    ss:insert(v)
                end
                return ss
            end,

            -- multi-value removal
            batch_remove = function(ss, ...)
                assert(is_set_impl(ss), 'self is not a set')
                for _,v in pairs {...} do
                    ss:remove(v)
                end
                return ss
            end,

            -- membership test
            has = function(ss, e)
                assert(is_set_impl(ss), 'self is not a set')
                return r[e] ~= nil
            end,
        },

        -- set manipulation operators

        -- union
        __add = function(s1, s2)
            assert(is_set_impl(s1), 'first argument is not a set')
            assert(is_set_impl(s2), 'second argument is not a set')
            local r = new()
            for _,v in ipairs(s1) do r:insert(v) end
            for _,v in ipairs(s2) do r:insert(v) end
            return r
        end,

        -- subtraction
        __sub = function(s1, s2)
            assert(is_set_impl(s1), 'first argument is not a set')
            assert(is_set_impl(s2), 'second argument is not a set')
            local r = new()
            for _,v in ipairs(s1) do r:insert(v) end
            for _,v in ipairs(s2) do r:remove(v) end
            return r
        end,

        -- intersection
        __mul = function(s1, s2)
            assert(is_set_impl(s1), 'first argument is not a set')
            assert(is_set_impl(s2), 'second argument is not a set')
            local r = new()
            for _,v in ipairs(s1) do
                if s2:has(v) then
                    r:insert(v)
                end
            end
            return r
        end,

        -- equality
        __eq = function(s1, s2)
            assert(is_set_impl(s1), 'first argument is not a set')
            assert(is_set_impl(s2), 'second argument is not a set')
            if #s1 ~= #s2 then return false
            else
                for _,v in ipairs(s1) do
                    if not s2:has(v) then return false end
                end
            end
            return true
        end,

        -- proper subset
        __lt = function(s1, s2)
            assert(is_set_impl(s1), 'first argument is not a set')
            assert(is_set_impl(s2), 'second argument is not a set')
            if s1 == s2 then return false
            else
                for _,v in ipairs(s1) do
                    if not s2:has(v) then return false end
                end
            end
            return true
        end,

        -- subset
        __le = function(s1, s2)
            assert(is_set_impl(s1), 'first argument is not a set')
            assert(is_set_impl(s2), 'second argument is not a set')
            return (s1 == s2) or (s1 < s2)
        end,

        -- write protection
        __newindex = function(ss, k, v)
            assert(is_set_impl(ss), 'first argument is not a set')
            if protected then
                error('attempting to write directly to set', 2)
            else
                rawset(ss, k, v)
            end
        end,

        -- metatable type tag
        __type__ = 'set'
    })
    s:batch_insert(...)
    instantiated = true
    return s
end

return setmetatable({}, { __call = new })
end
end

do
local _ENV = _ENV
package.preload[ "stringx" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
  the full license in the file LICENSE or by following the web link.

  STRINGX - extensions to string operations in Lua.

  This is a collection of miscellaneous routines to use as a drop-in replacement
  for the standard string library.  For convenience, the standard string library
  is shadowed thus permitting the following in code:

    local string = require 'stringx'

  This lets all current string operations get used in parallel with the
  extensions in a seamless fashion.
--############################################################################]]

--[[####################################
  Import required modules.
--####################################]]
local string = require 'string'

--[[####################################
  Copy required functions.
--####################################]]
local str_format = string.format
local str_gmatch = string.gmatch
local str_gsub = string.gsub
local str_len = string.len
local str_match = string.match

--[[####################################
  Useful patterns.
--####################################]]
local pure_ascii = '[\0-\x7f]+'


--[[####################################
  Shadow the standard library.
--####################################]]
local mt = {
    __index = string
}
local M = setmetatable({}, mt)

--[[####################################
  Assorted supplemental string operations.
--####################################]]

--[[################
  Concatenate a list of strings with a separator between members.
--################]]
local function concat(mem, sep)
    local n = #mem
    local ret = ''

    if n > 0 then
        ret = mem[1]
        for i = 2, n do
            ret = ret .. sep .. mem[i]
        end
    end

    return ret
end
M.concat = concat

--[[################
  Count the number of characters in a string which match a given pattern.
--################]]
local function count_matching(str, pat)
    local count = 0
    for match in str_gmatch(str, pat) do
        count = count + str_len(match)
    end
    return count
end
M.count_matching = count_matching

--[[################
  Extract the characters in a string which match a given pattern.
--################]]
local function extract_matching(str, pat)
    local rv = ""
    for match in str_gmatch(str, pat) do
        rv = rv .. match
    end
    return rv
end
M.extract_matching = extract_matching

--[[################
  Split a string according to a separator pattern.
--################]]
local function split(str, sep)
    local lsep = sep or ':'
    local mem = {}
    local pat = str_format('([^%s]+)', lsep)

    str_gsub(str, pat, function(ch) mem[#mem + 1] = ch end)

    return mem
end
M.split = split

--[[################
  Trim a string of leading and closing white.
--################]]
local function trim(str)
    str_match(str, '^[%s]*(.-)[%s]*$')
end
M.trim = trim

return setmetatable({}, { __index = M })
end
end

do
local _ENV = _ENV
package.preload[ "tablex" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
  the full license in the file LICENSE or by following the web link.

  TABLEX - extensions to table operations in Lua.

  This is a collection of miscellaneous routines to use as a drop-in replacement
  for the standard table library.  For convenience, the standard table library
  is shadowed thus permitting the following in code:

    local table = require 'tablex'

  This lets all current table operations get used in parallel with the
  extensions in a seamless fashion.
  ############################################################################]]

--[[####################################
  Shadow the standard library.
--####################################]]
local tbl = table
local mt = {
  __index = tbl
}
local M = setmetatable({}, mt)

--[[####################################
  Helpful local utilities
--####################################]]

--[[################
  Define a total ordering for keys:
  - numbers first
  - then strings
  - then all other key types, ordered as if strings
--################]]
local function order(k1, k2)
    local type_k1 = type(k1)
    local type_k2 = type(k2)
    if (type_k1 == 'string' and type_k2 == 'string') or
       (type_k1 == 'number' and type_k2 == 'number') then
        return k1 < k2
    elseif type_k1 == 'number' and type_k2 ~= 'number' then
        return true
    elseif type_k1 == 'string' and type_k2 ~= 'number' then
        return false
    else
        return tostring(k1) < tostring(k2)
    end
end

--[[####################################
  Table shuffling API.
--####################################]]

--[[################
  In-place array shuffle.  shufflei(a) takes an array-flavoured table, shuffles
  it in-place, then returns that table (for convenient chaining, nesting, etc.).
--################]]
local function shufflei(a)
  assert(type(a) == 'table', 'can only shuffle tables')
  local i
  for i = 1, #a do
    local s = math.random(i, #a)
    a[i],a[s] = a[s],a[i]
  end
  return a
end

--[[################
  Shuffle.  shuffle(a) takes an array-flavoured table, copies it, shuffles the
  copy and returns it.
--################]]
local function shuffle(a)
  assert(type(a) == 'table', 'can only shuffle tables')
  local r = {}
  for _,v in ipairs(a) do tbl.insert(r, v) end
  return shufflei(r)
end

--[[################
  Shuffle in-place.  kvshufflei(t) takes a table of K-V pairs (a general table)
  and mixes up the values such that the former K-V relationships are mixed up,
  analogous to how they'd be mixed up in a numerically-indexed table's shuffling.
--################]]
local function kvshufflei(t)
  assert(type(t) == 'table', 'can only shuffle tables')
  local ks = {}
  local vs = {}
  for k,v in pairs(t) do
    tbl.insert(ks, k)
    tbl.insert(vs, v)
  end
  shufflei(ks)
  local i
  for i = 1, #ks do
    t[ks[i]] = vs[i]
  end
  return t
end

--[[################
  Shuffle.  kvshuffle(t) takes a table of K-V pairs (a general table)  and
  mixes up the values such that the former K-V relationships are mixed up,
  analogous to how they'd be mixed up in a numerically-indexed table's shuffling.
--################]]
local function kvshuffle(t)
  assert(type(t) == 'table', 'can only shuffle tables')
  local r = {}
  for k,v in pairs(t) do r[k] = v end
  return kvshufflei(r)
end

M.kvshuffle = kvshuffle
M.kvshufflei = kvshufflei
M.shuffle = shuffle
M.shufflei = shufflei

--[[####################################
  Sorted-order K-V table iterator.
  - Can cope with key/value deletion mid-traversal.
  - Can cope with key/value addition mid-traversal but will not traverse added
    keys.
  - CANNOT cope with keys that are not sortable in table.sort() unless a custom
    comparator is provided.
--####################################]]

--[[################
  Iterator.
--################]]
local function spairs(t, c)

    local function make_snext()
        local function sindex()
            local skeys = {}

            for k in pairs(t) do
                tbl.insert(skeys, k)
            end

            tbl.sort(skeys, c)

            local i, j = 1, #skeys

            while i < j do
                skeys[i], skeys[j] = skeys[j], skeys[i]
                i = i + 1
                j = j - 1
            end

            return skeys
        end

        local states = setmetatable(sindex(), {__mode = 'k'})

        local function snext(s)
            local k = tbl.remove(states)

            if k == nil then return end

            local v = rawget(t, k)
            if v ~= nil then
                return k, v
            else
                return snext(s)
            end
        end

        return snext, t, nil
    end

    return make_snext()
end

M.spairs = spairs

--[[####################################
  General table utilities.
--####################################]]

--[[################
  Dump a table's contents (including metatables) recursively.
--################]]
local function dump(t, n, i)
    assert(type(t) == 'table', 'can only dump tables')

    local indent = i or 0
    local function dump_value(k, v)
        return string.rep(' ', indent) .. tostring(k) .. ' -> ' .. tostring(v) .. '\n'
    end

    local function dump_table(n, t)
        local name = n or '(unnamed)'
        local rv = ''
        rv = rv .. string.rep(' ', indent) .. tostring(name) .. ' -> ' .. tostring(t) .. '\n'
        indent = indent + 2
        for k,v in spairs(t, order) do
--        for k,v in pairs(t) do
            if type(v) == 'table' then
                rv = rv .. dump(v, k, indent)
            else
                rv = rv .. dump_value(k, v)
            end
        end
        return rv
    end

    return dump_table(n, t)
end

--[[################
  Execute a function for each element of a table.
  This should have been in the higher-order section, but functions here need it
  declared in advance without the clumsiness of forward declaration
--################]]
local function foreach(t, f, ...)
  for k,v in pairs(t) do
    f(k, v, ...)
  end
end

--[[################
  Count the number of times a given element appears in a table.
--################]]
local function count(t, e)
  local r = 0
  foreach(t, function(_,v) if v == e then r = r + 1 end end)
  return r
end

--[[################
  Find the first key (searched in arbitrary order) whose value matches an
  element.
--################]]
local function find(t, e)
  for k,v in pairs(t) do
    if v == e then
      return k
    end
  end
end

--[[################
  Return the number of entries in a table.
--################]]
local function size(t)
  local r = 0
  for _,_ in pairs(t) do
    r = r + 1
  end
  return r
end

M.dump = dump
M.count = count
M.find = find
M.size = size

--[[####################################
  General array utilities.
--####################################]]

--[[################
  Slice an array-flavoured table, left and right.
--################]]
local function slice(t, l, r)
  if l < 0      then error 'cannot skip negative numbers of arguments' end
  if r < 0      then error 'cannot drop negative numbers of arguments' end
  if l + r > #t then error 'cannot slice more elements than arguments' end
  return { tbl.unpack(t, l+1, #t-r) }
end

--[[################
  Skip the first elements of an array-flavoured table.
--################]]
local function skip(t, l)
  return slice(t, l, 0)
end

--[[################
  Drop the last elements of an array-flavoured table.
--################]]
local function drop(t, r)
  return slice(t, 0, r)
end

M.slice = slice
M.skip = skip
M.drop = drop

--[[####################################
  Higher-order operations.
--####################################]]

-- note that foreach belongs here, but we use it earlier and local predeclaration syntax is clunky

--[[################
  Map a function to a table's values.
--################]]
local function map(t, f)
  assert(type(t) == 'table', 'can only map over tables')
  local r = {}
  foreach(t, function(k,v)
    r[k] = f(v)
  end)
  return r
end

--[[################
  Filter values by a user-provided predicate.
--################]]
local function filter(t, p, ...)
  assert(type(t) == 'table', 'can only filter tables')
  local r = {}
  foreach(t, function(k, v, ...)
    if p(k, v, ...) then
        r[k] = v
    end
  end)
  return r
end

M.map = map
M.filter = filter
M.foreach = foreach

return setmetatable({}, { __index = M })
end
end

do
local _ENV = _ENV
package.preload[ "tag" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
  the full license in the file LICENSE or by following the web link.

  TAG - arbitrary tagging facilities for tables and userdata

  Notes on implementation:

  I've resisted the urge to use tag to add a metatable with the set operations
  bundled inside simply as being too heavyweight for such a lightweight concept.
  `tag` is implemented in the simplest, most straightforward way possible
  without being obtrusive on the value's behaviour anywhere else.  Thus the tags
  themselves are, indeed, stored in the metatable, but the operations are
  external.
--############################################################################]]

local set = require 'set'

local function add(t, ...)
    old_mt = getmetatable(t) or {}
    old_mt.__tag_table = old_mt.__tag_table or set()
    old_mt.__tag_table:batch_insert(...)
    setmetatable(t, old_mt)
end

local function remove(t, ...)
    old_mt = getmetatable(t) or {}
    old_mt.__tag_table = old_mt.__tag_table or set()
    old_mt.__tag_table:batch_remove(...)
    setmetatable(t, old_mt)
end

local function has_tag(t, v)
    return  (getmetatable(t) and getmetatable(t).__tag_table and getmetatable(t).__tag_table:has(v)) or false
end

return setmetatable({}, {
    __index = {
        add = add,
        remove = remove,
        has_tag = has_tag,
    }
})
end
end

do
local _ENV = _ENV
package.preload[ "utils" ] = function( ... ) local arg = _G.arg;
--[[## ft=lua ##################################################################
  This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
  the full license in the file LICENSE or by following the web link.

  UTILS - a grab bag of homeless features waiting a proper packaging.
--############################################################################]]

local table = require 'table'

local M = {}

--[[#####################################
  A wrapper around `require()` that tries to require a package and, failing
  this, falls back to a provided default.  This is useful for speculatively
  loading wrapper modules and degrading gracefully, e.g.:

        ```
        local table = requirex('tablex', 'table')

        -- ...

        if table.dump then table.dump(foo) end
        ```

--####################################]]
local function requirex(module, default)
  local e, m, f = pcall(require, module)
  if e then return m, f else return require(default) end
end

--[[####################################
  A wrapper around the usual error pattern of returning an error code and a
  value as a pairing.  If the error code is falsey, the value is the error
  message (which is printed) and the third value, if present, is used as the
  OS return code (true if not present).
--####################################]]
local function error_check(err, val, code)
  if not err then
    print(val)
    os.exit(code or false, true)
  end
  return val
end

--[[####################################
  Defers errors without interrupting tasks. Especially useful in test suites.
  Returns a table that contains a list of errors encountered and two functions
  for tracking and displaying both:
    - :add(message) - adds an error message to the list
    - :check() - checks for the existence of any errors and throws the whole
                 list if any have been reported.
  Note that there can be several different error_handlers active in a program.
  Each exists independently and independently tracks its own list of errors.
--####################################]]
local function error_handler()
    local errors = {}
    return {
        add = function(_self, e)
            table.insert(errors, e)
        end,
        check = function(self)
            if #errors > 0 then
                error('Errors recorded:\n\t- ' .. table.concat(errors, '\n\t- '), 0)
            end
        end
    }
end

--[[####################################
  Adjust a decimal number `n` to `d` significant digits.
--####################################]]
local function significant_digits(n, d)
  assert(type(n) == 'number' and type(d) == 'number', 'both arguments must be numbers')
  assert(math.type(d) == 'integer' and d > 0,         'second argument must be positive integer')

  local mul   = 0
  local div   = 0
  local rc    = n
  local shift = math.modf(math.log10(n))
  if shift < 0 then
    shift = shift - d
  else
    shift = shift - d + 1
  end

  rc = rc * 10 ^ -shift
  rc = math.modf(rc + 0.5)
  rc = rc * 10 ^ shift

  if math.type(n) == 'integer' then
    rc = math.modf(rc)
  end

  return rc
end

M.error_check = error_check
M.error_handler = error_handler
M.requirex = requirex
M.significant_digits = significant_digits

return M
end
end

